﻿/**
 *       Project: ServerLib
 *     Copyright: Copyright (C) 2008, Rob Kennedy
 *   Create Date: 2008-07-15 3:40 PM
 * Modified Date: 2008-07-15 3:40 PM    RK  Added comments
 *      Comments: Ignore SA1633.
 *      
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace RBLCheckPlugin
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Linq;
    using System.Net.Sockets;
    using ServerLib.Core;
    using ServerLib.Data;
    using ServerLib.Plugin;

    /// --------------------------------------------------------------------
    /// Project	 : RBLPlugin
    /// Class	 : IServicePlugin
    /// Inherits : N/A
    /// --------------------------------------------------------------------
    /// <summary>
    /// A real-time blackhole list checking plugin used for confirming that
    /// a remote client is not on one or more RBL lookup tables.
    /// </summary>
    /// <authorname>Rob Kennedy</authorname>
    /// <remarks>
    /// </remarks>
    /// --------------------------------------------------------------------
    public class RBLPlugin : IServicePlugin
    {
        #region Private Properties

        /// <summary>Contains the plugin full name</summary>
        private string pluginName = "Real-Time Blackhole List Check Plugin";

        /// <summary>Contain the plugin unique key name</summary>
        private string pluginKey = "RBLCheckPlugin";

        /// <summary>Contains the plugin type</summary>
        private PluginType pluginType = PluginType.Connection;

        /// <summary>Contains a collection of services that use the plugin library</summary>
        private List<PluginService> services = new List<PluginService>();

        /// <summary>Contains a collection of unique plugin specific attribute settings</summary>
        private List<PluginAttribute> attributes = new List<PluginAttribute>();

        /// <summary>Contains an array of RBL server names used to check the passed address against</summary>
        private string[] rblServerNames = { string.Empty };

        /// <summary>Contains the error message returned by the plugin library</summary>
        private string errorMessage = string.Empty;

        /// <summary>Contains the error code returned by the plugin</summary>
        private int errorCode = 0;

        #endregion

        #region IServicePlugin Members

        /// <summary>
        /// Gets the plugin library title
        /// </summary>
        public string Name
        {
            get 
            { 
                return this.pluginName; 
            }
        }

        /// <summary>
        /// Gets the plugin library key
        /// </summary>
        public string Key
        {
            get 
            { 
                return this.pluginKey; 
            }
        }

        /// <summary>
        /// Gets or sets the Plugin library Type code
        /// </summary>
        public PluginType Type
        {
            get
            {
                return this.pluginType;
            }

            set
            {
                this.pluginType = value;
            }
        }

        /// <summary>
        /// Gets the error message returned by the plugin library
        /// </summary>
        public string ErrorMessage
        { 
            get 
            { 
                return this.errorMessage; 
            } 
        }

        /// <summary>
        /// Gets the error code returned by the plugin library
        /// </summary>
        public int ErrorCode
        {
            get 
            { 
                return this.errorCode; 
            }
        }

        /// <summary>
        /// Gets or sets the services that use the plugin library
        /// </summary>
        public List<PluginService> Services
        {
            get
            {
                return this.services;
            }
            
            set
            {
                this.services = value;
            }
        }

        /// <summary>
        /// Gets or sets a collection of unique plugin specific attribute settings
        /// </summary>
        public List<PluginAttribute> Attributes
        {
            get 
            { 
                return this.attributes; 
            }
            
            set 
            { 
                this.attributes = value; 
            }
        }

        /// <summary>
        /// Executes the plugin library code and returns a response code
        /// </summary>
        /// <param name="context">Plugin context object containing context information</param>
        /// <returns>Returns a PluginResponse code for an appropriate service action</returns>
        public PluginResponse DoAction(IServicePluginContext context)
        {
            PluginResponse resultValue = PluginResponse.Continue;

            if (context.DB != null)
            {
                bool wasConnected = context.DB.IsConnected;

                if (!wasConnected)
                {
                    context.DB.Open();
                }

                if (context.DB.IsConnected)
                {
                    Logging.DataConnection = context.DB;
                    Logging.ServiceName = context.LoggingServiceName;

                    IEnumerable<PluginAttribute> attrib = from attr in this.Attributes
                                                          where attr.Name.ToLower() == "rbllist"
                                                          select attr;
                    if (attrib != null ? attrib.Count<PluginAttribute>() > 0 : false)
                    {
                        PluginAttribute rblAttrib = attrib.ElementAt<PluginAttribute>(0);
                        this.rblServerNames = rblAttrib.Value.Split(',');
                    }

                    // build reversed address name 
                    string[] octets = context.ClientTcp.RemoteIP.Split('.');
                    Array.Reverse(octets);
                    string reversedAddress = string.Join(".", octets);

                    foreach (string rblServer in this.rblServerNames)
                    {
                        try
                        {
                            // Logging.WriteLog(string.Format("RBL-Plugin: Lookup for {0}.{1}", reversedAddress, rblServer), true, false, context.Verbose);
                            // check this address against specified rbl servers as o4.o3.o2.o1.rblservername.net
                            IPAddress[] addresses = Dns.GetHostAddresses(string.Format("{0}.{1}", reversedAddress, rblServer));
                            if (addresses != null ? addresses.Length > 0 : false)
                            {
                                resultValue = PluginResponse.DisconnectWithError;
                                this.errorCode = 550;
                                this.errorMessage = string.Format("{0} {1}", Properties.Resources.RblErrorMessage, rblServer);
                                break;
                            }
                        }
                        catch (SocketException sex)
                        {
                            // DNS not found, means IP is ok, otherwise, write socket error
                            if (sex.ErrorCode != 11001)
                            {
                                // Logging.WriteLog(string.Format("RBL-Plugin: Socket Error occured: {0}", sex.Message), true, false, context.Verbose);
                            }
                        }
                        catch (Exception ex)
                        {
                            // error occured attempting to connect; 
                            // Logging.WriteLog(string.Format("RBL-Plugin: Error occured: {0}", ex.Message), true, false, context.Verbose);
                        }
                    }
                }

                if (!wasConnected)
                {
                    context.DB.Close();
                }
            }

            return resultValue;
        }

        #endregion
    }
}
